/*
 *  This file is part of Bracket Properties
 *  Copyright 2011 David R. Smith
 *
 */

package asia.redact.bracket.properties;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * Populate a Properties object. Parser is a thread-safe one use object. The list of
 * tokens normally is the output of a PropertiesLexer but can be generated in other ways.
 * 
 * @author Dave
 * @see PropertiesLexer
 */
public class PropertiesParser {
	
	final List<PropertiesToken> tokens;
	int index;
	final Properties properties;
	private final Lock lock = new ReentrantLock();

	public PropertiesParser(List<PropertiesToken> tokens, Properties properties) {
		super();
		this.tokens = tokens;
		this.properties = properties;
	}
	
	public PropertiesParser(List<PropertiesToken> tokens) {
		super();
		this.tokens = tokens;
		this.properties = new PropertiesImpl();
	}
	
	public void parse(){
		lock.lock();
		try {
			String key = null;
			ValueModel value = new ValueModel();
			while(index<tokens.size()){
				PropertiesToken t = la(0);
				//System.out.println(t);
				
				if(t.type==PropertiesTokenType.COMMENT){
					value.addComment(t.text);
					index++;
					index++; // consume the line break on the comment
					continue;
				}
				
				// consume an empty line
				if(t.type==PropertiesTokenType.NATURAL_LINE_BREAK){
					index++;
					continue;
				}
				
				if(t.type==PropertiesTokenType.KEY){
					key = t.text;
					PropertiesToken sep = la(1);
					value.setSeparator(sep.text.charAt(0));
					PropertiesToken val = la(2);
					if(val.type==PropertiesTokenType.NATURAL_LINE_BREAK){
						// key with no value
						value.addValue("");
						properties.getPropertyMap().put(key, value);
						index+=3; // consumes the line break
						value = new ValueModel();
						continue;
					}
					
					value.addValue(val.text);
					
					index+=3;
					
					PropertiesToken lineEnd = la(0);
				
					if(lineEnd.type==PropertiesTokenType.LOGICAL_LINE_BREAK){
						List<String> found = scanAheadForLogicalValues();
						for(String s:found) value.addValue(s);
						index+=(found.size()*2);//consume all additional logical values and their seps
						properties.getPropertyMap().put(key, value);
						value = new ValueModel();
						continue;
					}else if(lineEnd.type==PropertiesTokenType.NATURAL_LINE_BREAK){
						properties.getPropertyMap().put(key, value);
						value = new ValueModel();
						continue;
					}else if(lineEnd.type==PropertiesTokenType.EOF){
						properties.getPropertyMap().put(key, value);
						value = new ValueModel();
						continue;
					}
				}
				
				index++;
			}
		}finally{
			lock.unlock();
		}
	}
	
	private PropertiesToken la(int amt){
		lock.lock();
		try {
			try{
				return tokens.get(index+amt);
			}catch(IndexOutOfBoundsException x){
				return PropertiesToken.eof();
			}
		}finally{
			lock.unlock();
		}
	}
	
	private List<String> scanAheadForLogicalValues(){
		lock.lock();
		try {
			List<String> list = new ArrayList<String>();
			while(true){
				PropertiesToken la0 = la(0);
				PropertiesToken la1 = la(1);
				if(	la0!= null
					&& la0.type==PropertiesTokenType.LOGICAL_LINE_BREAK
					&& la1 != null
					&& la1.type==PropertiesTokenType.VALUE){
						String val = la1.text;
						list.add(val);
					    index+=2;
				}else{
					return list;
				}
			}
		}finally{
			lock.unlock();
		}
	}

	public Properties getProperties() {
		return properties;
	}
	
}
